//
//  String+ZJ.swift
//  ZJSwiftKit
//
//  Created by zyx on 2023/2/16.
//

import Foundation
import CoreLocation

// MARK: - Properties
extension ZJPrefix where Base == String {
    
   /// 文本中是否包含 Emoji
   public var containEmoji: Bool {
        // http://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji
        for scalar in base.unicodeScalars {
            switch scalar.value {
            case 0x1F600...0x1F64F, // Emoticons
                 0x1F300...0x1F5FF, // Misc Symbols and Pictographs
                 0x1F680...0x1F6FF, // Transport and Map
                 0x1F1E6...0x1F1FF, // Regional country flags
                 0x2600...0x26FF, // Misc symbols
                 0x2700...0x27BF, // Dingbats
                 0xE0020...0xE007F, // Tags
                 0xFE00...0xFE0F, // Variation Selectors
                 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs
                 127_000...127_600, // Various asian characters
                 65024...65039, // Variation selector
                 9100...9300, // Misc items
                 8400...8447: // Combining Diacritical Marks for Symbols
                return true
            default:
                continue
            }
        }
        return false
    }
    
    
    /// 检查字符串是否是有效的电子邮件格式。
    public var isValidEmail: Bool {
        // http://emailregex.com/
        let regex =
            "^(?:[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}0-9](?:[a-z0-9-]*[\\p{L}0-9])?\\.)+[\\p{L}0-9](?:[\\p{L}0-9-]*[\\p{L}0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[\\p{L}0-9-]*[\\p{L}0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$"
        return base.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
    }
    
    /// String转换成时间戳
    public var toTimeIntervalString : String {
        let dateformat = DateFormatter()
        dateformat.dateFormat = "yyyy-MM-dd HH:mm:ss"
        let timeDate = dateformat.date(from: base)
        return "\(timeDate?.timeIntervalSince1970 ?? 0)"
    }
    
    /// 时间戳转换为时间
    public var toDateString : String {
        return base.zj.toDouble.zj.toDateString
    }
    
    /// String转换为URL
    public var toURL: URL? {
        URL(string: base)
    }
    
    /// 转换为Int，转换不成功返回0
    public var toInt : Int {
        let scan : Scanner = Scanner(string: base)
        var intValue : Int = 0
        if scan.scanInt(&intValue) && scan.isAtEnd {
            return intValue
        }
        return 0
    }
    
    /// 转换为Double，转换不成功返回0
    public var toDouble : Double {
        let scan : Scanner = Scanner(string: base)
        var doubleValue : Double = 0
        if #available(iOS 13.0, *) {
            doubleValue = scan.scanDouble() ?? 0
            if scan.isAtEnd {
                return doubleValue
            }else{
                return 0
            }
        } else {
            if scan.scanDouble(&doubleValue) && scan.isAtEnd {
                return doubleValue
            }
            return 0
        }
    }
    
    /// 转换为Float， 转换不成功返回0
    public var toFloat : Float {
        let scan : Scanner = Scanner(string: base)
        var floatValue : Float = 0
        if #available(iOS 13.0, *) {
            floatValue = scan.scanFloat() ?? 0
            if scan.isAtEnd {
                return floatValue
            }else{
                return 0
            }
        } else {
            if scan.scanFloat(&floatValue) && scan.isAtEnd {
                return floatValue
            }
            return 0
        }
    }
    
    /// 转换为Bool，true/yes/1 均为true， 不区分大小写
    public var toBool : Bool {
        if base.lowercased() == "true" || base.lowercased() == "yes" || base == "1" {
            return true
        } else {
            return false
        }
    }
    
    /// url格式化
    public var urlEncoding : String {
        return base.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? base
    }
    
    /// 字符串经纬度转换为CLLocationCoordinate2D，格式为 "latitude , longitude"、"latitude / longitude"、"latitude | longitude"空格自动过滤，转换失败返回nil
    public var toCoordinate2D: CLLocationCoordinate2D? {
        var components: [String] = []
        if base.contains(",") {
            components = base.components(separatedBy: ",")
        } else if base.contains("|") {
            components = base.components(separatedBy: "|")
        } else if base.contains("/") {
            components = base.components(separatedBy: "/")
        }
        if components.count != 2 { return nil }
        let strLat = components[0].trimmingCharacters(in: .whitespacesAndNewlines)
        let strLng = components[1].trimmingCharacters(in: .whitespacesAndNewlines)
        return CLLocationCoordinate2D(latitude: strLat.zj.toDouble, longitude: strLng.zj.toDouble)
    }
}

// MARK: - 字符串替换 -
extension ZJPrefix where Base == String {
    
    /// 字符串替换字符串
    /// - Parameters:
    ///   - original: 原字符串
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
   public func replace<Target>(in original: Target, with replacement: Target) -> String where Target: StringProtocol {
        return base.replacingOccurrences(of: original, with: replacement)
    }
    
    
    /// 替换第几个字符串
    /// - Parameters:
    ///   - replaceIndex: 替换的Index
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
    public func replace<Target>(in replaceIndex: Int, with replacement: Target) -> String where Target: StringProtocol {
        let stringIndex = base.index(base.startIndex, offsetBy: replaceIndex)
        return base.replacingCharacters(in: stringIndex...stringIndex, with: replacement)
    }
    
    
    /// 替换某个区间字符串
    /// - Parameters:
    ///   - bounds: 区间（例如：1..<3）
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
    public func replace<Target>(in bounds: CountableRange<Int>, with replacement: Target) -> String where Target: StringProtocol {
        let start = base.index(base.startIndex, offsetBy: bounds.lowerBound)
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < start { return base}
        return base.replacingCharacters(in: start..<end, with: replacement)
    }
    
    /// 替换某个区间字符串
    /// - Parameters:
    ///   - bounds: 区间（例如：1...3）
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
    public func replace<Target>(in bounds: CountableClosedRange<Int>, with replacement: Target) -> String where Target: StringProtocol {
        let start = base.index(base.startIndex, offsetBy: bounds.lowerBound)
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < start { return base}
        return base.replacingCharacters(in: start...end, with: replacement)
    }
    
    /// 替换某个区间字符串
    /// - Parameters:
    ///   - bounds: 区间（例如：1...）
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
    public func replace<Target>(in bounds: CountablePartialRangeFrom<Int>, with replacement: Target) -> String where Target: StringProtocol {
        let start = base.index(base.startIndex, offsetBy: bounds.lowerBound)
        let end = base.index(base.endIndex, offsetBy: -1)
        if end < start { return base}
        return base.replacingCharacters(in: start...end, with: replacement)
    }
    
    /// 替换某个区间字符串
    /// - Parameters:
    ///   - bounds: 区间（例如：...3）
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
    public func replace<Target>(in bounds: PartialRangeThrough<Int>, with replacement: Target) -> String where Target: StringProtocol {
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < base.startIndex { return base }
        return base.replacingCharacters(in: base.startIndex...end, with: replacement)
    }
    
    /// 替换某个区间字符串
    /// - Parameters:
    ///   - bounds: 区间（例如：..<3）
    ///   - replacement: 新字符串
    /// - Returns: 替换后的字符串
    public func replace<Target>(in bounds: PartialRangeUpTo<Int>, with replacement: Target) -> String where Target: StringProtocol {
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < base.startIndex { return base }
        return base.replacingCharacters(in: base.startIndex..<end, with: replacement)
    }
}

// MARK: - JSON
extension ZJPrefix where Base == String {
    
    /// JSONString转换为字典
    public func jsonToDictionary() -> Dictionary<AnyHashable, Any>? {
        
        guard  let jsonData: Data = base.data(using: .utf8) else {
            return nil
        }
        
        let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
        return dict as? Dictionary<AnyHashable, Any>
        
    }
    
    /// JSONString转换Array
    public func jsonToArray() -> [Any]? {
        
        guard  let jsonData:Data = base.data(using: .utf8) else {
            return [Any]()
        }
        let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
        
        return dict as? [Any]
    }
}

// MARK: - String与日期之间转换 -

extension ZJPrefix where Base == String {
    /// 转换成date类型
    /// - Parameter format: 格式，默认为"yyyy-MM-dd"
    /// - Returns: Date时间
    public func toDate(format: String = "yyyy-MM-dd") -> Date? {
        let df = DateFormatter()
        df.dateFormat = format
        return df.date(from: base)
    }
    
    /// 判断是否包含字符数组中的某一个字符
    /// - Parameter collection: 字符数组
    /// - Returns: 是否包含
    public func contains(_ collection: [Base]) -> Bool {
        for str in collection {
            if base.contains(str) {
                return true
            }
        }
        return false
    }
    
    /// 判断当前字符是否等于字符数组中的某一个字符
    /// - Parameter collection: 字符数组
    /// - Returns: 是否等于某一个字符
    public func isEqualTo(_ collection: [Base]) -> Bool {
        for str in collection {
            if base == str {
                return true
            }
        }
        return false
    }
    
    
    /// 查询两个字符之间的字符串
    /// - Parameters:
    ///   - of: 起点字符串
    ///   - to: 终点字符串
    /// - Returns: 字符串,字符串的位置
    public func firstRang(of: String, to: String) throws -> (label: String, range: NSRange) {
        do {
            let pattern = "(?<=\(of)).+?(?=\(to))"
            let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
            let range = NSRange(base.startIndex..<base.endIndex, in: base)
            let matches = regex.matches(in: base, options: [], range: range)
            if !matches.isEmpty, let match = matches.first, let range = Range(match.range, in: base) {
                let subString = String(base[range])
                return (subString, match.range)
            }
            throw NSError(domain: "查找不到", code: -1)
        } catch {
            throw NSError(domain: "查找不到", code: -1)
        }
    }
    
    /// 查询字符串中指定字符的所有位置
    /// - Parameter searchString: 查找的字符串
    /// - Returns: 所有的位置
    public func ranges(of searchString: String) -> [NSRange] {
        var ranges = [NSRange]()
        let string = base as NSString
        var searchRange = NSRange(location: 0, length: string.length)
        
        while searchRange.location != NSNotFound {
            searchRange = string.range(of: searchString, options: [], range: searchRange)
            if searchRange.location != NSNotFound {
                ranges.append(searchRange)
                searchRange.location = searchRange.location + searchRange.length
                searchRange.length = string.length - searchRange.location
            }
        }
        return ranges
    }
}

// MARK: - 字符串下标取法 -

extension ZJPrefix where Base == String {
    
    public subscript (i: Int) -> Character {
        return base[base.index(base.startIndex, offsetBy: i)]
    }
    
    public subscript (bounds: CountableRange<Int>) -> Substring {
        let start = base.index(base.startIndex, offsetBy: bounds.lowerBound)
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < start { return "" }
        return base[start..<end]
    }
    
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        let start = base.index(base.startIndex, offsetBy: bounds.lowerBound)
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < start { return "" }
        return base[start...end]
    }
    
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        let start = base.index(base.startIndex, offsetBy: bounds.lowerBound)
        let end = base.index(base.endIndex, offsetBy: -1)
        if end < start { return "" }
        return base[start...end]
    }
    
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < base.startIndex { return "" }
        return base[base.startIndex...end]
    }
    
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        let end = base.index(base.startIndex, offsetBy: bounds.upperBound)
        if end < base.startIndex { return "" }
        return base[base.startIndex..<end]
    }
}

// MARK: - 字符串与UIColor转换 -
extension ZJPrefix where Base == String {
    
    /// 十六进制字符串颜色转为UIColor，“FFFFFF”、“0xFFFFFF”、"0XFFFFFF"、"#FFFFFF"格式，不区分大小写
    /// - Parameter alpha: 透明度
    public func color(alpha: CGFloat = 1.0) -> UIColor {
        // 存储转换后的数值
        var red: UInt64 = 0, green: UInt64 = 0, blue: UInt64 = 0
        var hex = base
        // 如果传入的十六进制颜色有前缀，去掉前缀
        if hex.hasPrefix("0x") || hex.hasPrefix("0X") {
            hex = String(hex[hex.index(hex.startIndex, offsetBy: 2)...])
        } else if hex.hasPrefix("#") {
            hex = String(hex[hex.index(hex.startIndex, offsetBy: 1)...])
        }
        // 如果传入的字符数量不足6位按照后边都为0处理，当然你也可以进行其它操作
        if hex.count < 6 {
            for _ in 0..<6-hex.count {
                hex += "0"
            }
        }
        // 分别进行转换
        // 红
        Scanner(string: String(hex[..<hex.index(hex.startIndex, offsetBy: 2)])).scanHexInt64(&red)
        // 绿
        Scanner(string: String(hex[hex.index(hex.startIndex, offsetBy: 2)..<hex.index(hex.startIndex, offsetBy: 4)])).scanHexInt64(&green)
        // 蓝
        Scanner(string: String(hex[hex.index(base.startIndex, offsetBy: 4)...])).scanHexInt64(&blue)
        return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: alpha)
    }
     
     /// RGB字符串转换为颜色 "255,255,255"或"255"
     /// - Parameter alpha: alpha值
     /// - Returns: UIColor颜色
    public func rgbColor(alpha: CGFloat = 1.0) -> UIColor {
         let colorArray = base.components(separatedBy: ",")
         var red : CGFloat = 0,green : CGFloat = 0, blue : CGFloat = 0
         if colorArray.count == 3 {
             red = CGFloat(colorArray[0].zj.toInt)
             green = CGFloat(colorArray[1].zj.toInt)
             blue = CGFloat(colorArray[2].zj.toInt)
         }else if colorArray.count == 2 {
             red = CGFloat(colorArray[0].zj.toInt)
             green = CGFloat(colorArray[1].zj.toInt)
         }else if colorArray.count == 1 {
             red = CGFloat(colorArray[0].zj.toInt)
             green = CGFloat(colorArray[0].zj.toInt)
             blue = CGFloat(colorArray[0].zj.toInt)
         }
        return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha)
     }
}


// MARK: -  String to image
extension ZJPrefix where Base == String {
    
    /// String to image
    /// - Parameters:
    ///   - bundle: bundle description
    ///   - traitCollection: traitCollection description
    /// - Returns: image maay be nil
    public func toImage(in bundle: Bundle? = Bundle.main, compatibleWith traitCollection: UITraitCollection? = nil) -> UIImage? {
      return  UIImage.init(named: base, in: bundle, compatibleWith: traitCollection)
    }
}



/// Extend String with `zj` proxy.
extension String: ZJPrefixProtocol { }
